#微信公众号：学长带你飞
#专注路径优化问题求解。
import numpy as np
import random

class gwo():
    def __init__(self,generation,popsize,to,oh,work,job_num):
        self.generation=generation                  #迭代次数
        self.popsize = popsize                      # 种群规模
        self.to=to
        self.oh=oh
        self.work=work
        self.job_num=job_num
    def to_MT(self,W1,M1,T1): #把加工机器编码和加工时间编码转化为对应列表，目的是记录工件的加工时间和加工机器
        Ma_W1,Tm_W1,WCross=[],[],[]
        for i in range(self.job_num):#添加工件个数的空列表
            Ma_W1.append([]),Tm_W1.append([]),WCross.append([]);
        for i in range(W1.shape[1]):
            signal1=int(W1[0,i])-1
            Ma_W1[signal1].append(M1[0,i]),Tm_W1[signal1].append(T1[0,i]); #记录每个工件的加工机器
            index=np.random.randint(0,2,1)[0]
            WCross[signal1].append(index)       #随机生成一个为0或者1的列表，用于后续的机器的均匀交叉
        return Ma_W1,Tm_W1,WCross
    def back_MT(self,W1,Ma_W1,Tm_W1):  #列表返回机器及加工时间编码
        memory1=np.zeros((1,self.job_num),dtype=np.int)
        m1,t1=np.zeros((1,W1.shape[1])),np.zeros((1,W1.shape[1]))
        for i in range(W1.shape[1]):
            signal1=int(W1[0,i])-1
            m1[0,i]=Ma_W1[signal1][memory1[0,signal1]] #读取对应工序的加工机器
            t1[0,i]=Tm_W1[signal1][memory1[0,signal1]]
            memory1[0,signal1]+=1
        return m1,t1
    def mac_cross(self,Ma_W1,Tm_W1,Ma_W2,Tm_W2,WCross):  #机器均匀交叉
        MC1,MC2,TC1,TC2=[],[],[],[]
        for i in range(self.job_num):     
            MC1.append([]),MC2.append([]),TC1.append([]),TC2.append([]);
            for j in range(len(WCross[i])):
                if(WCross[i][j]==0):  #为0时继承另一个父代的加工机器选择
                    MC1[i].append(Ma_W1[i][j]),MC2[i].append(Ma_W2[i][j]),TC1[i].append(Tm_W1[i][j]),TC2[i].append(Tm_W2[i][j]);
                else:                #为1时继承父代的机器选择
                    MC2[i].append(Ma_W1[i][j]),MC1[i].append(Ma_W2[i][j]),TC2[i].append(Tm_W1[i][j]),TC1[i].append(Tm_W2[i][j]);
        return MC1,TC1,MC2,TC2
    def gwo_total(self):
        answer=[]
        fit_every=[[],[],[],[]]
        job_init=np.zeros((self.popsize,len(self.work)))
        work_job1,work_M1,work_T1=np.zeros((self.popsize,len(self.work))),np.zeros((self.popsize,len(self.work))),np.zeros((self.popsize,len(self.work)))
        work_job,work_M,work_T=np.zeros((self.popsize,len(self.work))),np.zeros((self.popsize,len(self.work))),np.zeros((self.popsize,len(self.work)))
        for gen in range(self.generation):
            if(gen<1):                      #第一次生成多个可行的工序编码，机器编码，时间编码
                for i in range(self.popsize):
                    job,machine,machine_time,initial_a=self.to.creat_job()
                    C_finish,Twork,E_all,_,_,_,_=self.to.caculate(job,machine,machine_time)
                    answer.append([C_finish,Twork,E_all])
                    work_job[i],work_M[i],work_T[i]=job[0],machine[0],machine_time[0]
                    job_init[i]=initial_a

                front,crowd,crowder=self.oh.dis(answer)    #计算分层，拥挤度，种群排序结果
                # print(front)
                # print(crowder)
                signal=front[0]
                pareto=np.array(answer)[signal].tolist()
                pareto_job,pareto_machine,pareto_time=work_job[signal],work_M[signal],work_T[signal]
                x=[pareto[i][0] for i in range(len(pareto))]
                y=[pareto[i][1] for i in range(len(pareto))]
                z=[pareto[i][2] for i in range(len(pareto))]
                fit_every[3].append(gen)
                fit_every[0].append([min(x),sum(x)/len(x),max(x)])
                fit_every[1].append([min(y),sum(y)/len(y),max(y)])
                fit_every[2].append([min(z),sum(z)/len(z),max(z)])

                
            index_sort=crowder
            work_job1,work_M1,work_T1=work_job[index_sort],work_M[index_sort],work_T[index_sort]
            answer1=np.array(answer)[index_sort].tolist()
            job_init1=job_init[index_sort]
            
            Alpha=job_init1[0]   #α狼
            Beta=job_init1[1]    #β狼
            Delta=job_init1[2]   #δ狼
            a = 2*(1-gen/self.generation)
            
            for i in range(3,self.popsize):     #用最优位置进行工序编码的更新
                job,machine,machine_time=work_job1[i:i+1],work_M1[i:i+1],work_T1[i:i+1]
                Ma_W1,Tm_W1,WCross=self.to_MT(job,machine,machine_time)
                x=job_init1[i]

                r1 = random.random()   #灰狼算法解的更新
                r2 = random.random()
                A1 = 2 * a * r1 - a
                C1 = 2 * r2
                D_alpha =C1*Alpha-x
                x1 = x - A1 * D_alpha

                r1 = random.random()
                r2 = random.random()
                A2 = 2 * a * r1 - a
                C2 = 2 * r2
                D_beta =C2*Beta-x
                x2 = x - A2 * D_beta

                r1 = random.random()
                r2 = random.random()
                A3 = 2 * a * r1 - a
                C3 = 2 * r2
                D_delta =C3*Delta-x
                x3 = x - A3 * D_alpha

                initial_a=(x1+x2+x3)/3   #更新公式
                index_work=initial_a.argsort()
                job_new=[]
                for j in range(len(self.work)):
                    job_new.append(self.work[index_work[j]])
                job_new=np.array(job_new).reshape(1,len(self.work))
                machine_new,time_new=self.back_MT(job_new,Ma_W1,Tm_W1)
                C_finish,Twork,E_all,_,_,_,_=self.to.caculate(job_new,machine_new,time_new)
                
                work_job1[i]=job_new[0]  #更新工序编码
                job_init1[i]=initial_a
                work_M1[i],work_T1[i]=machine_new[0],time_new[0]
                answer1[i]=[C_finish,Twork,E_all]
            # for i in range(0,self.popsize,2):
            #     job,machine,machine_time=work_job1[i:i+1],work_M1[i:i+1],work_T1[i:i+1]
            #     Ma_W1,Tm_W1,WCross=self.to_MT(job,machine,machine_time)
            #     job1,machine1,machine_time1=work_job1[i+1:i+2],work_M1[i+1:i+2],work_T1[i+1:i+2]
            #     Ma_W2,Tm_W2,WCross=self.to_MT(job1,machine1,machine_time1)

            #     MC1,TC1,MC2,TC2=self.mac_cross(Ma_W1,Tm_W1,Ma_W2,Tm_W2,WCross)
            #     machine_new,time_new=self.back_MT(job,MC1,TC1)
            #     C_finish,Twork,E_all,_,_,_,_=self.to.caculate(job,machine_new,time_new)
            #     #更新机器和加工时间编码
            #     work_M1[i]=machine_new[0]
            #     work_T1[i]=time_new[0]
            #     answer1[i]=[C_finish,Twork,E_all]
            #     machine_new1,time_new1=self.back_MT(job1,MC2,TC2)
            #     C_finish,Twork,E_all,_,_,_,_=self.to.caculate(job1,machine_new1,time_new1)
            #     #更新机器和加工时间编码
            #     work_M1[i+1]=machine_new1[0]
            #     work_T1[i+1]=time_new1[0]
            #     answer1[i+1]=[C_finish,Twork,E_all]
            work_job,work_M,work_T=work_job1,work_M1,work_T1
            answer=answer1
            job_init=job_init1

            front,crowd,crowder=self.oh.dis(answer) 
            signal=front[0]
            pareto=np.array(answer)[signal].tolist()
            pareto_job,pareto_machine,pareto_time=work_job[signal],work_M[signal],work_T[signal]
            x=[pareto[i][0] for i in range(len(pareto))]
            y=[pareto[i][1] for i in range(len(pareto))]
            z=[pareto[i][2] for i in range(len(pareto))]
            fit_every[3].append(gen+1)
            fit_every[0].append([min(x),sum(x)/len(x),max(x)])
            fit_every[1].append([min(y),sum(y)/len(y),max(y)])
            fit_every[2].append([min(z),sum(z)/len(z),max(z)])
            print('算法迭代到了第%.0f次'%(gen+1))
            
        return pareto,pareto_job,pareto_machine,pareto_time,fit_every  #返回pareto解及其编码
